#ifndef __U2Vector4_H__
#define __U2Vector4_H__

#include "U2PreRequest.h"
#include "U2Vector3.h"


U2EG_NAMESPACE_BEGIN


/** 4-dimensional homogeneous vector.
*/
class _U2Share U2Vector4
{
public:
    u2real x, y, z, w;

public:
    inline U2Vector4()
    {
    }

    inline U2Vector4( const u2real fX, const u2real fY, const u2real fZ, const u2real fW )
        : x( fX ), y( fY ), z( fZ ), w( fW)
    {
    }

    inline explicit U2Vector4( const u2real afCoordinate[4] )
        : x( afCoordinate[0] ),
          y( afCoordinate[1] ),
          z( afCoordinate[2] ),
          w( afCoordinate[3] )
    {
    }

    inline explicit U2Vector4( const int afCoordinate[4] )
    {
        x = (u2real)afCoordinate[0];
        y = (u2real)afCoordinate[1];
        z = (u2real)afCoordinate[2];
        w = (u2real)afCoordinate[3];
    }

    inline explicit U2Vector4( u2real* const r )
        : x( r[0] ), y( r[1] ), z( r[2] ), w( r[3] )
    {
    }

    inline explicit U2Vector4( const u2real scaler )
        : x( scaler )
        , y( scaler )
        , z( scaler )
        , w( scaler )
    {
    }

    inline explicit U2Vector4(const U2Vector3& rhs)
        : x(rhs.x), y(rhs.y), z(rhs.z), w(1.0f)
    {
    }

	/** Exchange the contents of this vector with another. 
	*/
	inline void swap(U2Vector4& other)
	{
		std::swap(x, other.x);
		std::swap(y, other.y);
		std::swap(z, other.z);
		std::swap(w, other.w);
	}

	inline u2real operator [] ( const size_t i ) const
    {
        assert( i < 4 );

        return *(&x+i);
    }

	inline u2real& operator [] ( const size_t i )
    {
        assert( i < 4 );

        return *(&x+i);
    }

	/// Pointer accessor for direct copying
	inline u2real* ptr()
	{
		return &x;
	}
	/// Pointer accessor for direct copying
	inline const u2real* ptr() const
	{
		return &x;
	}

    /** Assigns the value of the other vector.
        @param
            rkVector The other vector
    */
    inline U2Vector4& operator = ( const U2Vector4& rkVector )
    {
        x = rkVector.x;
        y = rkVector.y;
        z = rkVector.z;
        w = rkVector.w;

        return *this;
    }

	inline U2Vector4& operator = ( const u2real fScalar)
	{
		x = fScalar;
		y = fScalar;
		z = fScalar;
		w = fScalar;
		return *this;
	}

    inline bool operator == ( const U2Vector4& rkVector ) const
    {
        return ( x == rkVector.x &&
            y == rkVector.y &&
            z == rkVector.z &&
            w == rkVector.w );
    }

    inline bool operator != ( const U2Vector4& rkVector ) const
    {
        return ( x != rkVector.x ||
            y != rkVector.y ||
            z != rkVector.z ||
            w != rkVector.w );
    }

    inline U2Vector4& operator = (const U2Vector3& rhs)
    {
        x = rhs.x;
        y = rhs.y;
        z = rhs.z;
        w = 1.0f;
        return *this;
    }

    // arithmetic operations
    inline U2Vector4 operator + ( const U2Vector4& rkVector ) const
    {
        return U2Vector4(
            x + rkVector.x,
            y + rkVector.y,
            z + rkVector.z,
            w + rkVector.w);
    }

    inline U2Vector4 operator - ( const U2Vector4& rkVector ) const
    {
        return U2Vector4(
            x - rkVector.x,
            y - rkVector.y,
            z - rkVector.z,
            w - rkVector.w);
    }

    inline U2Vector4 operator * ( const u2real fScalar ) const
    {
        return U2Vector4(
            x * fScalar,
            y * fScalar,
            z * fScalar,
            w * fScalar);
    }

    inline U2Vector4 operator * ( const U2Vector4& rhs) const
    {
        return U2Vector4(
            rhs.x * x,
            rhs.y * y,
            rhs.z * z,
            rhs.w * w);
    }

    inline U2Vector4 operator / ( const u2real fScalar ) const
    {
        assert( fScalar != 0.0 );

        u2real fInv = 1.0f / fScalar;

        return U2Vector4(
            x * fInv,
            y * fInv,
            z * fInv,
            w * fInv);
    }

    inline U2Vector4 operator / ( const U2Vector4& rhs) const
    {
        return U2Vector4(
            x / rhs.x,
            y / rhs.y,
            z / rhs.z,
            w / rhs.w);
    }

    inline const U2Vector4& operator + () const
    {
        return *this;
    }

    inline U2Vector4 operator - () const
    {
        return U2Vector4(-x, -y, -z, -w);
    }

    inline friend U2Vector4 operator * ( const u2real fScalar, const U2Vector4& rkVector )
    {
        return U2Vector4(
            fScalar * rkVector.x,
            fScalar * rkVector.y,
            fScalar * rkVector.z,
            fScalar * rkVector.w);
    }

    inline friend U2Vector4 operator / ( const u2real fScalar, const U2Vector4& rkVector )
    {
        return U2Vector4(
            fScalar / rkVector.x,
            fScalar / rkVector.y,
            fScalar / rkVector.z,
            fScalar / rkVector.w);
    }

    inline friend U2Vector4 operator + (const U2Vector4& lhs, const u2real rhs)
    {
        return U2Vector4(
            lhs.x + rhs,
            lhs.y + rhs,
            lhs.z + rhs,
            lhs.w + rhs);
    }

    inline friend U2Vector4 operator + (const u2real lhs, const U2Vector4& rhs)
    {
        return U2Vector4(
            lhs + rhs.x,
            lhs + rhs.y,
            lhs + rhs.z,
            lhs + rhs.w);
    }

    inline friend U2Vector4 operator - (const U2Vector4& lhs, u2real rhs)
    {
        return U2Vector4(
            lhs.x - rhs,
            lhs.y - rhs,
            lhs.z - rhs,
            lhs.w - rhs);
    }

    inline friend U2Vector4 operator - (const u2real lhs, const U2Vector4& rhs)
    {
        return U2Vector4(
            lhs - rhs.x,
            lhs - rhs.y,
            lhs - rhs.z,
            lhs - rhs.w);
    }

    // arithmetic updates
    inline U2Vector4& operator += ( const U2Vector4& rkVector )
    {
        x += rkVector.x;
        y += rkVector.y;
        z += rkVector.z;
        w += rkVector.w;

        return *this;
    }

    inline U2Vector4& operator -= ( const U2Vector4& rkVector )
    {
        x -= rkVector.x;
        y -= rkVector.y;
        z -= rkVector.z;
        w -= rkVector.w;

        return *this;
    }

    inline U2Vector4& operator *= ( const u2real fScalar )
    {
        x *= fScalar;
        y *= fScalar;
        z *= fScalar;
        w *= fScalar;
        return *this;
    }

    inline U2Vector4& operator += ( const u2real fScalar )
    {
        x += fScalar;
        y += fScalar;
        z += fScalar;
        w += fScalar;
        return *this;
    }

    inline U2Vector4& operator -= ( const u2real fScalar )
    {
        x -= fScalar;
        y -= fScalar;
        z -= fScalar;
        w -= fScalar;
        return *this;
    }

    inline U2Vector4& operator *= ( const U2Vector4& rkVector )
    {
        x *= rkVector.x;
        y *= rkVector.y;
        z *= rkVector.z;
        w *= rkVector.w;

        return *this;
    }

    inline U2Vector4& operator /= ( const u2real fScalar )
    {
        assert( fScalar != 0.0 );

        u2real fInv = 1.0f / fScalar;

        x *= fInv;
        y *= fInv;
        z *= fInv;
        w *= fInv;

        return *this;
    }

    inline U2Vector4& operator /= ( const U2Vector4& rkVector )
    {
        x /= rkVector.x;
        y /= rkVector.y;
        z /= rkVector.z;
        w /= rkVector.w;

        return *this;
    }

    /** Calculates the dot (scalar) product of this vector with another.
        @param
            vec Vector with which to calculate the dot product (together
            with this one).
        @returns
            A float representing the dot product value.
    */
    inline u2real dotProduct(const U2Vector4& vec) const
    {
        return x * vec.x + y * vec.y + z * vec.z + w * vec.w;
    }
	/// Check whether this vector contains valid values
	inline bool isNaN() const
	{
		return U2Math::isNaN(x) || U2Math::isNaN(y) || U2Math::isNaN(z) || U2Math::isNaN(w);
	}
    /** Function for writing to a stream.
    */
    inline _U2Share friend std::ostream& operator <<
        ( std::ostream& o, const U2Vector4& v )
    {
        o << "U2Vector4(" << v.x << ", " << v.y << ", " << v.z << ", " << v.w << ")";
        return o;
    }
    // special
    static const U2Vector4 ZERO;
};


U2EG_NAMESPACE_END


#endif

